/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
         http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY const char *KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

//
// Created by WangYunlai on 2023/06/28.
//

#include <sstream>
#include "sql/parser/value.h"
#include "storage/field/field.h"
#include "common/log/log.h"
#include "common/lang/comparator.h"
#include "common/lang/string.h"
#include "util/util.h"

const char *ATTR_TYPE_NAME[] = {"undefined", "chars", "ints", "floats", "dates", "booleans","list","nulls"};

const char *attr_type_to_string(AttrType type)
{
  if (type >= UNDEFINED && type <= NULLS) {
    return ATTR_TYPE_NAME[type];
  }
  return "unknown";
}
AttrType attr_type_from_string(const char *s)
{
  for (unsigned int i = 0; i < sizeof(ATTR_TYPE_NAME) / sizeof(ATTR_TYPE_NAME[0]); i++) {
    if (0 == strcmp(ATTR_TYPE_NAME[i], s)) {
      return (AttrType)i;
    }
  }
  return UNDEFINED;
}
bool match(const char *src, const char *pattern)
{
  int src_offset     = 0;
  int pattern_offset = 0;
  int src_len        = strlen(src);
  int pattern_len    = strlen(pattern);
  while (src_offset < src_len && pattern_offset < pattern_len) {
    char src_char     = src[src_offset];
    char pattern_char = pattern[pattern_offset];
    // 处理'_'
    if (pattern_char == '_') {
      src_offset++;
      pattern_offset++;
    }
    // 处理'%'
    else if (pattern_char == '%') {
      pattern_offset++;
      if (pattern_offset == pattern_len) {
        src_offset = src_len;
        break;
      }
      pattern_char = pattern[pattern_offset];
      while (src_offset < src_len) {
        if (pattern_char != '_' && pattern_char != '%') {
          while (src_offset < src_len && src_char != pattern_char) {
            src_char = src[++src_offset];
          }
          if (src_offset == src_len)
            break;
        }
        bool sub_match = match(src + src_offset, pattern + pattern_offset);
        if (!sub_match)
          src_offset++;
        else {
          src_offset     = src_len;
          pattern_offset = pattern_len;
        }
      }
    }
    // 处理其他字符
    else {
      if (src_char == pattern_char) {
        src_offset++;
        pattern_offset++;
      } else {
        break;
      }
    }
  }
  while (pattern_offset < pattern_len) {
    char pattern_char = pattern[pattern_offset];
    if (pattern_char == '%')
      pattern_offset++;
    else
      break;
  }
  return (src_offset == src_len) && (pattern_offset == pattern_len);
}
Value::Value(int val) { set_int(val); }

Value::Value(float val) { set_float(val); }

Value::Value(bool val) { set_boolean(val); }

Value::Value(const char *s, int len /*= 0*/) { set_string(s, len); }

Value::Value(int val, bool isDate)
{
  if (isDate) {
    set_date(val);
  } else {
    set_date(-1);
  }
}

void Value::set_data(char *data, int length)
{
  switch (attr_type_) {
    case CHARS: {
      set_string(data, length);
    } break;
    case INTS: {
      num_value_.int_value_ = *(int *)data;
      length_               = length;
    } break;
    case FLOATS: {
      num_value_.float_value_ = *(float *)data;
      length_                 = length;
    } break;
    case BOOLEANS: {
      num_value_.bool_value_ = *(int *)data != 0;
      length_                = length;
    } break;
    case DATES: {
      num_value_.int_value_ = *(int *)data;
      length_               = length;
    } break;
    default: {
      LOG_WARN("unknown data type: %d", attr_type_);
    } break;
  }
}
void Value::set_int(int val)
{
  attr_type_            = INTS;
  num_value_.int_value_ = val;
  length_               = sizeof(val);
}

void Value::set_float(float val)
{
  attr_type_              = FLOATS;
  num_value_.float_value_ = val;
  length_                 = sizeof(val);
}
void Value::set_boolean(bool val)
{
  attr_type_             = BOOLEANS;
  num_value_.bool_value_ = val;
  length_                = sizeof(val);
}
void Value::set_string(const char *s, int len /*= 0*/)
{
  attr_type_ = CHARS;
  if (len > 0) {
    len = strnlen(s, len);
    str_value_.assign(s, len);
  } else {
    str_value_.assign(s);
  }
  length_ = str_value_.length();
}

void Value::set_date(int val)
{
  attr_type_            = DATES;
  num_value_.int_value_ = val;
  length_               = sizeof(val);
}
void Value::set_list(const std::vector<Value>& value_list){
  attr_type_=LIST;
  list_value_=value_list;
}
void Value::set_value(const Value &value)
{
  switch (value.attr_type_) {
    case INTS: {
      set_int(value.get_int());
    } break;
    case FLOATS: {
      set_float(value.get_float());
    } break;
    case CHARS: {
      set_string(value.get_string().c_str());
    } break;
    case BOOLEANS: {
      set_boolean(value.get_boolean());
    } break;
    case DATES: {
      set_date(value.get_date());
    } break;
    case LIST:{
      set_list(value.get_list());
    }
    case UNDEFINED: {
      ASSERT(false, "got an invalid value type");
    } break;
  }
}

const char *Value::data() const
{
  switch (attr_type_) {
    case CHARS: {
      return str_value_.c_str();
    } break;
    default: {
      return (const char *)&num_value_;
    } break;
  }
}

std::string Value::to_string() const
{
  std::stringstream os;
  switch (attr_type_) {
    case INTS: {
      os << num_value_.int_value_;
    } break;
    case FLOATS: {
      os << common::double_to_str(num_value_.float_value_);
    } break;
    case BOOLEANS: {
      os << num_value_.bool_value_;
    } break;
    case CHARS: {
      os << str_value_;
    } break;
    case DATES: {
      int value  = num_value_.int_value_;
      int yyyy_i = value / (32 * 16);
      int mm_i   = value / 32 % 16;
      int dd_i   = value % 32;
      os << yyyy_i / 1000 << yyyy_i / 100 % 10 << yyyy_i / 10 % 10 << yyyy_i % 10 << '-' << mm_i / 10 << mm_i % 10
         << '-' << dd_i / 10 << dd_i % 10;
    }break;
    case NULLS:{
      os<<"NULL";
    }
    default: {
      LOG_WARN("unsupported attr type: %d", attr_type_);
    } break;
  }
  return os.str();
}

int Value::compare(const Value &other) const
{
  if (this->attr_type_ == other.attr_type_) {
    switch (this->attr_type_) {
      case INTS: {
        return common::compare_int((void *)&this->num_value_.int_value_, (void *)&other.num_value_.int_value_);
      } break;
      case FLOATS: {
        return common::compare_float((void *)&this->num_value_.float_value_, (void *)&other.num_value_.float_value_);
      } break;
      case CHARS: {
        return common::compare_string((void *)this->str_value_.c_str(),
            this->str_value_.length(),
            (void *)other.str_value_.c_str(),
            other.str_value_.length());
      } break;
      case BOOLEANS: {
        return common::compare_int((void *)&this->num_value_.bool_value_, (void *)&other.num_value_.bool_value_);
      }
      case DATES: {
        return common::compare_int((void *)&this->num_value_.int_value_, (void *)&other.num_value_.int_value_);
      }
      case LIST:{
        int this_size=list_value_.size();
        int other_size=other.list_value_.size();
        int size_compare=common::compare_int(&this_size,&other_size);
        if(size_compare!=0)return size_compare;
        int result=0;
        for(int i=0;i<this_size;i++){
          Value this_val=list_value_[i];
          Value other_val=other.list_value_[i];
          result=this_val.compare(other_val);
          if(result!=0)return result;
        }
        return result;
      }
      default: {
        LOG_WARN("unsupported type: %d", this->attr_type_);
      }
    }
  } else if (this->attr_type_ == INTS && other.attr_type_ == FLOATS) {
    float this_data = this->num_value_.int_value_;
    return common::compare_float((void *)&this_data, (void *)&other.num_value_.float_value_);
  } else if (this->attr_type_ == FLOATS && other.attr_type_ == INTS) {
    float other_data = other.num_value_.int_value_;
    return common::compare_float((void *)&this->num_value_.float_value_, (void *)&other_data);
  } else if (this->attr_type_ == INTS && other.attr_type_ == CHARS) {
    int other_data = other.get_int();
    return common::compare_int((void *)&this->num_value_.int_value_, (void *)&other_data);
  } else if (this->attr_type_ == CHARS && other.attr_type_ == INTS) {
    int this_data = get_int();
    return common::compare_int((void *)&this_data, (void *)&other.num_value_.int_value_);
  } else if (this->attr_type_ == FLOATS && other.attr_type_ == CHARS) {
    float other_data = other.get_float();
    return common::compare_float((void *)&this->num_value_.float_value_, (void *)&other_data);
  } else if (this->attr_type_ == CHARS && other.attr_type_ == FLOATS) {
    float this_data = get_float();
    return common::compare_float((void *)&this_data, (void *)&other.num_value_.float_value_);
  } else if (this->attr_type_ == INTS && other.attr_type_ == CHARS) {
    int other_data = other.get_int();
    return common::compare_int((void *)&this->num_value_.int_value_, (void *)&other_data);
  } else if (this->attr_type_ == CHARS && other.attr_type_ == INTS) {
    int this_data = get_int();
    return common::compare_int((void *)&this_data, (void *)&other.num_value_.int_value_);
  } else if (this->attr_type_ == FLOATS && other.attr_type_ == CHARS) {
    float other_data = other.get_float();
    return common::compare_float((void *)&this->num_value_.float_value_, (void *)&other_data);
  } else if (this->attr_type_ == CHARS && other.attr_type_ == FLOATS) {
    float this_data = get_float();
    return common::compare_float((void *)&this_data, (void *)&other.num_value_.float_value_);
  }
  else if (this->attr_type_==LIST){
    int size=list_value_.size();
    int result=-1;
    for(int i=0;i<size;i++){
      Value val=list_value_[i];
      result=val.compare(other);
      if(result!=0)return result;
    }
    return result;
  }
  else if (other.attr_type_==LIST){
    int size=other.list_value_.size();
    int result=-1;
    for(int i=0;i<size;i++){
      Value val=other.list_value_[i];
      result=compare(val);
      if(result!=0)return result;
    }
    return result;
  }
  LOG_WARN("not supported");
  return -1;  // TODO return rc?
}
int Value::like(const Value &other) const
{
  if (this->attr_type() != CHARS || other.attr_type() != CHARS)
    return 0;
  return match(this->data(), other.data());
}
bool Value::cast_to_type(AttrType type){
  if(type==attr_type_)return true;
  if(attr_type_==NULLS||attr_type_==LIST||attr_type_==BOOLEANS)return false;
  switch(type){
    case CHARS:{
      std::string str=this->to_string();
      this->set_string(str.c_str(),str.length());
    }break;
    case INTS:{
      int tmp=this->get_int();
      this->set_int(tmp);
    }break;
    case FLOATS:{
      float tmp=this->get_float();
      this->set_float(tmp);
    }break;
    default:{
      return false;
    }
  }
  return true;
}
int Value::get_int() const
{
  switch (attr_type_) {
    case CHARS: {
      try {
        int         tmp      = 0;
        const char *src_data = data();
        int         srcLen   = length();
        for (int i = 0; i < srcLen; i++) {
          const char this_data = src_data[i];
          if (this_data >= '0' && this_data <= '9') {
            int this_int = this_data - '0';
            tmp          = tmp * 10 + this_int;
          } else
            break;
        }
        return tmp;
      } catch (std::exception const &ex) {
        LOG_TRACE("failed to convert string to number. s=%s, ex=%s", str_value_.c_str(), ex.what());
        return 0;
      }
    }
    case INTS: {
      return num_value_.int_value_;
    }
    case FLOATS: {
      int tmp=(int)(num_value_.float_value_);
      if((num_value_.float_value_-tmp)>0.5){
        tmp+=1;
      }
      return tmp;
    }
    case BOOLEANS: {
      return (int)(num_value_.bool_value_);
    }
    case DATES: {
      return num_value_.int_value_;
    }
    default: {
      LOG_WARN("unknown data type. type=%d", attr_type_);
      return 0;
    }
  }
  return 0;
}

int Value::get_date() const { return get_int(); }

float Value::get_float() const
{
  switch (attr_type_) {
    case CHARS: {
      try {
        float       tmp      = 0;
        const char *src_data = data();
        int         srcLen   = length();
        bool        haveDot  = false;
        for (int i = 0; i < srcLen; i++) {
          const char this_data = src_data[i];
          if (this_data >= '0' && this_data <= '9') {
            int this_int = this_data - '0';
            if (haveDot) {
              tmp += this_int * 0.1;
            } else {
              tmp = tmp * 10 + this_int;
            }
          } else if (this_data == '.') {
            haveDot = true;
          } else {
            break;
          }
        }
        return tmp;

      } catch (std::exception const &ex) {
        LOG_TRACE("failed to convert string to float. s=%s, ex=%s", str_value_.c_str(), ex.what());
        return 0.0;
      }
    } break;
    case INTS: {
      return float(num_value_.int_value_);
    } break;
    case FLOATS: {
      return num_value_.float_value_;
    } break;
    case DATES: {
      return float(num_value_.int_value_);
    } break;
    case BOOLEANS: {
      return float(num_value_.bool_value_);
    } break;
    default: {
      LOG_WARN("unknown data type. type=%d", attr_type_);
      return 0;
    }
  }
  return 0;
}

std::string Value::get_string() const { return this->to_string(); }

bool Value::get_boolean() const
{
  switch (attr_type_) {
    case CHARS: {
      try {
        float val = std::stof(str_value_);
        if (val >= EPSILON || val <= -EPSILON) {
          return true;
        }

        int int_val = std::stol(str_value_);
        if (int_val != 0) {
          return true;
        }

        return !str_value_.empty();
      } catch (std::exception const &ex) {
        LOG_TRACE("failed to convert string to float or integer. s=%s, ex=%s", str_value_.c_str(), ex.what());
        return !str_value_.empty();
      }
    } break;
    case INTS: {
      return num_value_.int_value_ != 0;
    } break;
    case FLOATS: {
      float val = num_value_.float_value_;
      return val >= EPSILON || val <= -EPSILON;
    } break;
    case DATES: {
      return num_value_.int_value_ != 0;
    } break;
    case BOOLEANS: {
      return num_value_.bool_value_;
    } break;
    default: {
      LOG_WARN("unknown data type. type=%d", attr_type_);
      return false;
    }
  }
  return false;
}
std::vector<Value> Value::get_list() const{
  return list_value_;
}